package in.iqing.model.db;

import com.lidroid.xutils.db.sqlite.Selector;
import com.lidroid.xutils.exception.DbException;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import in.iqing.control.helper.LogHelper;
import in.iqing.model.bean.Book;
import in.iqing.model.bean.Chapter;
import in.iqing.model.bean.DownloadedBook;
import in.iqing.model.bean.History;
import in.iqing.model.bean.Volume;
import in.iqing.module.download.BookTask;
import in.iqing.module.download.ChapterTask;
import in.iqing.module.download.ContentTask;

/**
 * Created by neavo on 2014/6/25.
 */

public class DBHelper extends BaseDB {
    private static final String TAG = "DBHelper";
    private static DBHelper mInstance;
    private Object chapterMutex = new Object();

    public synchronized static DBHelper getInstance() {
        if (mInstance == null) {
            mInstance = new DBHelper();
        }
        return mInstance;
    }

    private DBHelper() {
        super();
    }

    public boolean hasSubscribe(Book book) {
        try {
            Book dbBook = db.findById(Book.class, book.getId());
            return dbBook != null && dbBook.isSubscribe();
        } catch (DbException e) {
            e.printStackTrace();
            return false;
        }
    }

    public void addSubscribe(Book book) {
        try {
            book.setSubscribe(true);
            Book dbBook = db.findById(Book.class, book.getId());
            if (dbBook == null) {
                db.save(book);
            } else {
                db.update(book, "subscribe");
            }
        } catch (DbException e) {
            e.printStackTrace();
        }
    }

    public void deleteSubscribe(Book book) {
        try {
            book.setSubscribe(false);
            db.update(book, "subscribe");
        } catch (DbException e) {
            e.printStackTrace();
        }
    }

    public List<Book> getSubscribedBooks() {
        try {
            return db.findAll(Selector.from(Book.class).where("subscribe", "=", true));
        } catch (DbException e) {
            e.printStackTrace();
            return null;
        }
    }

    public void setBookmark(int bookId, int chapterId, long bookmark) {
        LogHelper.warn(TAG, "setBookmark bookId:" + bookId + " chapterId:" + chapterId + " bookmark:" + bookmark);
        try {
            History mark = new History();
            mark.setTime(System.currentTimeMillis());
            mark.setBookId(bookId);
            mark.setChapterId(chapterId);
            mark.setBookmark(bookmark);
            db.saveOrUpdate(mark);
        } catch (DbException e) {
            e.printStackTrace();
        }
    }

    public History getBookmark(int bookId) {
        try {
            return db.findById(History.class, bookId);
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<Book> getHistoryBooks() {
        try {
            List<History> histories = db.findAll(Selector.from(History.class).orderBy("time", false));
            if (histories == null || histories.size() == 0) {
                return null;
            }
            List<Book> books = new ArrayList<>();
            for (History history : histories) {
                if (history.getBook() != null) {
                    books.add(history.getBook());
                }
            }
            return books;
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void saveBook(Book book) {
        try {
            if (book != null) {
                Book dbBook = db.findById(Book.class, book.getId());
                if (dbBook != null) {
                    book.setCachedChapterCount(dbBook.getCachedChapterCount());
                    book.setSubscribe(dbBook.isSubscribe());
                }
                db.saveOrUpdate(book);
                // TODO save book category
            }
        } catch (DbException e) {
            e.printStackTrace();
        }
    }

    public void saveBookDirectory(Book book, List<Volume> volumes) {
        try {
            for (Volume volume : volumes) {
                volume.setBookId(book.getId());
                db.saveOrUpdate(volume);
                for (Chapter chapter : volume.getChapters()) {
                    chapter.setBookId(book.getId());
                    chapter.setVolumeId(volume.getId());
                    db.saveOrUpdate(chapter);
                }
            }
        } catch (DbException e) {
            e.printStackTrace();
        }
    }

    private void saveNewChapter(Chapter chapter) {
        try {
            Chapter dbChapter = db.findById(Chapter.class, chapter.getId());
            if (dbChapter == null) {
                db.save(chapter);
            }
        } catch (DbException e) {
            e.printStackTrace();
        }
    }

    public List<Volume> getBookVolumes(Book book) {
        try {
//            Book dbBook = db.findById(Book.class, book.getId());
//            List<Volume> volumes = dbBook.getVolumes().getAllFromDb();
            List<Volume> volumes = db.findAll(
                    Selector.from(Volume.class).where("book_id", "=", book.getId()).orderBy("volume_order")
            );
            return volumes;
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void clearBookCacheCount(Book book) {
        try {
            book.setCachedChapterCount(0);
            db.update(book, "cached_chapter_count");
        } catch (DbException e) {
            e.printStackTrace();
        }
    }

    public List<Chapter> getVolumeChapters(Volume volume) {
        try {
            List<Chapter> chapters = db.findAll(
                    Selector.from(Chapter.class).where("volume_id", "=", volume.getId()).orderBy("chapter_order")
            );
            return chapters;
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void clearDownloadTask(Chapter chapter) {
        synchronized (chapterMutex) {
            ChapterTask chapterTasks = getChapterTask(chapter);
            if (chapterTasks == null) {
                LogHelper.warn(TAG, "chapter task empty, skip clear:" + chapter.getId());
                return;
            }
            List<ContentTask> contentTasks = getContentTask(chapter);
            if (contentTasks == null || contentTasks.size() == 0) {
                LogHelper.warn(TAG, "content task empty, skip clear:" + chapter.getId());
                return;
            }
            try {
                db.deleteAll(contentTasks);
                db.delete(chapterTasks);
            } catch (DbException e) {
                e.printStackTrace();
            }
        }
    }

    public ChapterTask getChapterTask(Chapter chapter) {
        try {
            return db.findFirst(Selector.from(ChapterTask.class)
                    .where("chapter_id", "=", chapter.getId()));
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

    public ChapterTask getChapterTask(int chapterId) {
        try {
            return db.findFirst(Selector.from(ChapterTask.class)
                    .where("chapter_id", "=", chapterId));
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

    public ChapterTask getChapterTask(ContentTask contentTask) {
        try {
            return db.findFirst(Selector.from(ChapterTask.class)
                    .where("chapter_id", "=", contentTask.getChapterId()));
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

    public List<ContentTask> getContentTask(Chapter chapter) {
        try {
            return db.findAll(Selector.from(ContentTask.class)
                    .where("chapter_id", "=", chapter.getId()));
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

    public ContentTask getStringContent(Chapter chapter) {
        try {
            return db.findFirst(Selector.from(ContentTask.class)
                    .where("chapter_id", "=", chapter.getId())
                    .and("type", "=", ContentTask.TYPE_STRING));
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

    public void increaseChapterTaskFinishCount(ContentTask contentTask) {
        ChapterTask chapterTask = DBHelper.getInstance().getChapterTask(contentTask);
        chapterTask.setFinishCount(chapterTask.getFinishCount() + 1);
        DBHelper.getInstance().save(chapterTask);
    }

    public void increaseBookCachedChapter(int bookId) {
        try {
            Book book = db.findById(Book.class, bookId);
            book.setCachedChapterCount(book.getCachedChapterCount() + 1);
            db.update(book, "cached_chapter_count");
        } catch (DbException e) {
            e.printStackTrace();
        }
    }

    public Set<Integer> getDownloadedChapters(Book book) {
        HashSet<Integer> chapters = new HashSet<>();
        try {

            List<ChapterTask> chapterTasks = db.findAll(Selector.from(ChapterTask.class).where("book_id", "=", book.getId()));
            if (chapterTasks != null && chapterTasks.size() > 0) {
                for (ChapterTask task : chapterTasks) {
                    if (task.getTotalCount() >= task.getFinishCount()) {
                        chapters.add(task.getChapterId());
                    }
                }
            }
        } catch (DbException e) {
            e.printStackTrace();
        }
        return chapters;
    }

    public BookTask getBookTask(Book book) {
        try {
            return db.findById(BookTask.class, book.getId());
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Book getBook(DownloadedBook downloadedBook) {
        try {
            return db.findById(Book.class, downloadedBook.getBookId());
        } catch (DbException e) {
            e.printStackTrace();
        }
        return null;
    }

}
